﻿using System.Collections.Immutable;
using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService.Symbols;

/// <summary>
///     可绑定类型符号的基类，包括函数定义、类方法定义、本地变量定义等。
/// </summary>
public abstract class BindableSymbol : AbstractTypeSymbol, IBindableSymbol
{
    private readonly WeakReference<TypeBinding> _binding;
    private readonly List<IDeclaration> _declarations;

    internal BindableSymbol(SymbolKinds kind,
        IDeclaration? declaration,
        ITypeSymbol? containingSymbol,
        ModuleSymbol? containingModule) :
        base(kind, TypeKinds.Unknown)
    {
        _declarations = [];
        ContainingSymbol = containingSymbol;
        ContainingModule = containingModule;
        _binding = new WeakReference<TypeBinding>(TypeBinding.Void);

        if (declaration == null)
        {
            return;
        }

        if (declaration.Is<BindableSignature>(out BindableSignature? signature))
        {
            _binding = new WeakReference<TypeBinding>(signature.Binding);
        }
        else if (declaration.Is<SignatureWithParameters>(out SignatureWithParameters? signatureWithParameters))
        {
            _binding = new WeakReference<TypeBinding>(signatureWithParameters.Binding);
        }

        _declarations.Add(declaration);
        LocationMap.AddLocation(declaration.GetLocation());
    }

    public override ModuleSymbol? ContainingModule { get; }


    protected ITypeSymbol? CachedBinding { get; private set; }

    public override string Name => Declaration?.Name ?? string.Empty;


    public bool IsNullable => (ModifierFlags & TypeSymbolModifierFlags.Nullable) != 0;

    public override bool IsBindable => true;

    public override IDeclaration? Declaration => _declarations.Count > 0 ? _declarations[0] : null;

    public override TypeKinds TypeKind => BindingSymbol.TypeKind;


    public ImmutableArray<IDeclaration> GetDeclarations()
    {
        return [.._declarations];
    }


    public override ITypeSymbol? ContainingSymbol { get; }


    public ITypeSymbol BindingSymbol => GetBindingSymbol();


    public ITypeSymbol? LookupNamedMember(string name, bool searchParent)
    {
        ITypeSymbol? symbol = NamedMembers.LookupLocalSymbol(name, searchParent) as ITypeSymbol;

        if (symbol != null || !searchParent)
        {
            return symbol;
        }

        ReadOnlySymbolTable? parent = NamedMembers.Parent;
        while (parent != null)
        {
            symbol = parent.LookupLocalSymbol(name, true) as ITypeSymbol;

            if (symbol != null)
            {
                return symbol;
            }

            parent = parent.Parent;
        }

        return symbol;
    }


    public ITypeSymbol? LookupNamedMember(Identifier identifier)
    {
        return LookupNamedMember(identifier.Value,
            identifier.GetFlags(SyntaxNodeFlags.OptionGlobalVariables));
    }


    public override bool IsAssignableFrom(ITypeSymbol type)
    {
        return BindingSymbol.IsAssignableFrom(type);
    }


    protected ITypeSymbol UpdateCachedBinding(ITypeSymbol symbol)
    {
        return CachedBinding = symbol;
    }


    public virtual ITypeSymbol GetBindingSymbol()
    {
        if (CachedBinding != null)
        {
            return CachedBinding;
        }

        if (!_binding.TryGetTarget(out TypeBinding? binding))
        {
            return UndefinedSymbol.Default;
        }

        ITypeSymbol? typeSymbol = null;
        // Void -> 对于声明函数，返回 Sub: ValueTypeSymbol.Void, Function: ValueTypeSymbol.Null
        //      -> 对于方法签名，返回 ValueTypeSymbol.Void
        //      -> 对于其他定义，返回 ValueTypeSymbol.Any
        if (binding == TypeBinding.Void)
        {
            typeSymbol = Declaration switch
            {
                // Sub Func() -> Void
                { IsSignature: true, DeclareKeywordToken.Kind: SyntaxKinds.SubKeywordToken } => ValueTypeSymbol.Void,
                // Function Func() -> Null
                { DeclareKeywordToken.Kind: SyntaxKinds.FunctionKeywordToken } => ValueTypeSymbol.Null,
                _ => ValueTypeSymbol.Any
            };

            return UpdateCachedBinding(typeSymbol);
        }

        string fullName = binding.FullQualifiedName.GetFullName();
        // 值类型
        if (ValueTypeSymbol.TryGetValueSymbol(fullName, out ValueTypeSymbol? basicSymbol))
        {
            typeSymbol = basicSymbol;
        }
        // 查找绑定类型的全名，优先向父级查找
        else
        {
            ModuleSymbol? parent = ContainingModule;
            while (parent != null)
            {
                ITypeSymbol? member = parent.LookupNamedMember(fullName);
                if (member != null)
                {
                    typeSymbol = member;
                    break;
                }

                parent = parent.ContainingModule;
            }

            if (typeSymbol == null &&
                GlobalSymbols.TryGetDeclaredSymbol(fullName, out ISymbol? declaredSymbol))
            {
                typeSymbol = (ITypeSymbol)declaredSymbol;
            }
        }

        if (typeSymbol == null)
        {
            return UndefinedSymbol.Default;
        }

        if (binding.Rank > 0 && typeSymbol.CanMakeArray)
        {
            return UpdateCachedBinding(typeSymbol.MakeArrayType(binding.Rank, null));
        }

        return UpdateCachedBinding(typeSymbol);
    }
}